実践!AWS CDK #26 Version 2
はじめに
AWS CDK の Version 2 がリリースされました。
今回は v2 の主な機能の紹介と、既存の CDK プロジェクトを v1 から v2 へ移行したいと思います。
前回の記事はこちら。
v1 から v2 の主な変更点
公式のドキュメントによると、主な変更点は以下 7 つです。
- ライブラリパッケージの単一化
aws-cdk-lib
パッケージに統合- 個々のパッケージのインストールが不要に
- 新しい L2 や L3 の Construct は aws-cdk-lib には含まれず個別のパッケージとして配布される
- 安定版として指定された後に aws-cdk-lib に移行
- 新しい機能が追加中の安定版モジュールでは新しい API に
Beta1
というサフィックスが付く- サフィックスが付いていないバージョンは、その API が安定版になった時に追加される
- Construct クラスは AWS CDK から別のライブラリとして抽出された
constructs
パッケージに分離
- v1 で Deprecated となっていたプロパティやメソッドは v2 で完全に削除された
- 削除された API のリストは GitHub で公開されている
- v1 の機能フラグ(feature flags)によって制限された動作は v2 ではデフォルトで有効になった
- 古い機能フラグはもはや不要で、殆どの場合はサポートされていない
- v2 ではデプロイ先の環境が最新のブートストラップスタックを使用して bootstrap されている必要がある
- レガシーのブートストラップスタックはサポートされなくなった
- 既存の環境をアップグレードするには再度
$ cdk bootstrap
を実行すれば良い
1 つ目の変更がとても嬉しいですね。
これまでは @aws-cdk/aws-ec2
や @aws-cdk/aws-iam
といったパッケージを必要に応じて次のように個別にインストールしていました。
$ npm install @aws-cdk/aws-ec2
上記コマンドの実行タイミングによっては、既にインストールされているパッケージ内の CDK バージョンとズレが発生してビルドが通らなくなるという問題がしばしば起こりました。今回のアップデートによりその場合のトラブルシューティングが不要となり、よりストレスなく CDK での開発に集中できるようになったということです。
やったぜ!
マイグレーション
それでは本シリーズでこれまで作成してきた CDK プロジェクトを実際に v2 へアップデートしていきましょう。
AWS CDK v2 のインストール
以下のコマンドを実行して CDK のバージョンを v2 にアップグレードします。
$ npm install -g aws-cdk
バージョンを確認して 2.0.0 になっていれば OK です。
$ cdk --version 2.0.0 (build 4b6ce31)
Bootstrap の実行
bootstrap コマンドを実行します。
$ cdk bootstrap ⏳ Bootstrapping environment aws://060405383263/ap-northeast-1... Trusted accounts for deployment: (none) Trusted accounts for lookup: (none) Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize. ✅ Environment aws://060405383263/ap-northeast-1 bootstrapped (no changes).
変更があれば自動で更新されますし、変更がなければ特に何も起きません。
機能フラグの削除
「機能フラグは不要」と書かれていたので、全て消してしまいましょう。
{ "app": "npx ts-node --prefer-ts-exts bin/devio.ts", "versionReporting": false, "context": { "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, "@aws-cdk/core:enableStackNameDuplicates": "true", "aws-cdk:enableDiffNoFail": "true", "@aws-cdk/core:stackRelativeExports": "true", "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, "@aws-cdk/aws-kms:defaultKeyPolicies": true, "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, "@aws-cdk/aws-efs:defaultEncryptionAtRest": true, "systemName": "devio", "envType": "stg", "keyName": "" } }
↓ 修正後
{ "app": "npx ts-node --prefer-ts-exts bin/devio.ts", "versionReporting": false, "context": { "systemName": "devio", "envType": "stg", "keyName": "" } }
dependencies の更新
package.json の記述を修正します。
{ "name": "devio", "version": "0.1.0", "bin": { "devio": "bin/devio.js" }, "scripts": { "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk" }, "devDependencies": { "@aws-cdk/assert": "^1.122.0", "@types/jest": "^26.0.23", "@types/node": "10.17.27", "aws-cdk": "1.122.0", "jest": "^26.6.3", "ts-jest": "^26.2.0", "ts-node": "^9.0.0", "typescript": "~3.9.7" }, "dependencies": { "@aws-cdk/aws-ec2": "^1.122.0", "@aws-cdk/aws-elasticloadbalancingv2": "^1.122.0", "@aws-cdk/aws-rds": "^1.122.0", "@aws-cdk/aws-secretsmanager": "^1.122.0", "@aws-cdk/core": "^1.122.0", "source-map-support": "^0.5.16" }, "jest": { "moduleFileExtensions": [ "js" ] } }
↓ 修正後
{ "name": "devio", "version": "0.1.0", "bin": { "devio": "bin/devio.js" }, "scripts": { "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk" }, "devDependencies": { "@types/jest": "^26.0.23", "@types/node": "10.17.27", "aws-cdk-lib": "^2.0.0", "constructs": "^10.0.0", "jest": "^26.6.3", "ts-jest": "^26.2.0", "ts-node": "^9.0.0", "typescript": "~3.9.7" }, "dependencies": { "aws-cdk-lib": "^2.0.0", "constructs": "^10.0.0", "source-map-support": "^0.5.16" }, "jest": { "moduleFileExtensions": [ "js" ] } }
dependencies 修正後、以下のコマンドを実行してパッケージも更新します。
$ npm install
この時点でビルドが通らなくなります。
import 文の更新
これまで @aws-cdk/core
やその他のパッケージを import していた部分を aws-cdk-lib
を参照するようにします。
また、Construct クラスは constructs パッケージに移行されたのでその書き換えも行います。
import * as cdk from '@aws-cdk/core'; import { Vpc } from './resource/vpc'; import { Subnet } from './resource/subnet'; import { InternetGateway } from './resource/internetGateway'; import { ElasticIp } from './resource/elasticIp'; import { NatGateway } from './resource/natGateway'; import { RouteTable } from './resource/routeTable'; import { NetworkAcl } from './resource/networkAcl'; import { IamRole } from './resource/iamRole'; import { SecurityGroup } from './resource/securityGroup'; import { Ec2 } from './resource/ec2'; import { Alb } from './resource/alb'; import { SecretsManager } from './resource/secretsManager'; import { Rds } from './resource/rds'; export class DevioStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // VPC const vpc = new Vpc(); vpc.createResources(this); ~ 省略 ~
↓ 修正後
import { Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { Vpc } from './resource/vpc'; import { Subnet } from './resource/subnet'; import { InternetGateway } from './resource/internetGateway'; import { ElasticIp } from './resource/elasticIp'; import { NatGateway } from './resource/natGateway'; import { RouteTable } from './resource/routeTable'; import { NetworkAcl } from './resource/networkAcl'; import { IamRole } from './resource/iamRole'; import { SecurityGroup } from './resource/securityGroup'; import { Ec2 } from './resource/ec2'; import { Alb } from './resource/alb'; import { SecretsManager } from './resource/secretsManager'; import { Rds } from './resource/rds'; export class DevioStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // VPC const vpc = new Vpc(); vpc.createResources(this); ~ 省略 ~
同様の修正をすべてのファイルに対し行います。(この時の commit は コチラ)
テストコードも修正します。
import { expect, haveResource } from '@aws-cdk/assert'; import * as cdk from '@aws-cdk/core'; import * as Devio from '../lib/devio-stack'; test('Context', () => { const app = new cdk.App({ context: { 'systemName': 'starwars', 'envType': 'prd' } }); const stack = new Devio.DevioStack(app, 'DevioStack'); expect(stack).to(countResources('AWS::EC2::VPC', 1)); expect(stack).to(haveResource('AWS::EC2::VPC', { Tags: [{ Key: 'Name', Value: 'starwars-prd-vpc' }] })); });
↓ 修正後
import { App } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import * as Devio from '../lib/devio-stack'; test('Context', () => { const app = new App({ context: { 'systemName': 'starwars', 'envType': 'prd' } }); const stack = new Devio.DevioStack(app, 'DevioStack'); const template = Template.fromStack(stack); template.resourceCountIs('AWS::EC2::VPC', 1); template.hasResourceProperties('AWS::EC2::VPC', { Tags: [{ Key: 'Name', Value: 'starwars-prd-vpc' }] }); });
テストコードは @aws-cdk/assert
ではなく aws-cdk-lib/assertions
というモジュールを使用するよう修正しました。
Stack オブジェクトを渡して Template オブジェクトを生成し、そのオブジェクトに対してチェックを行うような構成となっています。
以下、従来のテストコードで使用したメソッドとの対応表です。
@aws-cdk/assert | aws-cdk-lib/assertions | 説明 |
---|---|---|
countResources() |
resourceCountIs() |
リソース数のチェック |
haveResource() |
hasResourceProperties() |
プロパティのチェック |
haveResourceLike() |
hasResourceProperties() |
プロパティのチェック |
countResourcesLike() |
ー | リソース数とプロパティのチェック |
anything() |
Match.anyValue() |
任意の値を表現 |
countResourcesLike()
に関しては対応しているメソッドがなさそうだったので、hasResourceProperties()
で代用しています。(この時の commit は コチラ)
ここまでできたら一度テストを実行してみましょう。
v1 でのテスト実行コマンドは $ npm run build && npm test
でしたが、v2 からは $ tsc && npm test
というコマンドが推奨されているようです。
$ tsc && npm test > [email protected] test > jest PASS test/resource/internetGateway.test.ts (12.643 s) PASS test/resource/secretsManager.test.ts (12.695 s) PASS test/resource/securityGroup.test.ts (12.686 s) PASS test/resource/subnet.test.ts (12.718 s) PASS test/resource/networkAcl.test.ts (12.749 s) PASS test/resource/alb.test.ts (12.827 s) PASS test/resource/routeTable.test.ts (12.752 s) PASS test/resource/ec2.test.ts (12.864 s) PASS test/resource/iamRole.test.ts (12.844 s) PASS test/resource/natGateway.test.ts (12.845 s) PASS test/resource/rds.test.ts (12.965 s) PASS test/devio.test.ts PASS test/resource/vpc.test.ts PASS test/resource/elasticIp.test.ts Test Suites: 14 passed, 14 total Tests: 14 passed, 14 total Snapshots: 0 total Time: 14.749 s, estimated 18 s Ran all test suites.
すべてのテストが通りました。
CFn テンプレートの出力
テンプレートを出力します。
$ cdk synth Resources: Vpc: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: Name Value: devio-stg-vpc ~ 省略 ~ RdsDbInstance1c: Type: AWS::RDS::DBInstance Properties: DBInstanceClass: db.r5.large AutoMinorVersionUpgrade: false AvailabilityZone: ap-northeast-1c DBClusterIdentifier: Ref: RdsDbCluster DBInstanceIdentifier: devio-stg-rds-instance-1c DBParameterGroupName: Ref: RdsDbParameterGroup DBSubnetGroupName: Ref: RdsDbSubnetGroup EnablePerformanceInsights: true Engine: aurora-mysql MonitoringInterval: 60 MonitoringRoleArn: Fn::GetAtt: - RoleRds - Arn PerformanceInsightsRetentionPeriod: 7 PreferredMaintenanceWindow: sun:20:30-sun:21:00 Parameters: BootstrapVersion: Type: AWS::SSM::Parameter::Value<String> Default: /cdk-bootstrap/hnb659fds/version Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip] Rules: CheckBootstrapVersion: Assertions: - Assert: Fn::Not: - Fn::Contains: - - "1" - "2" - "3" - "4" - "5" - Ref: BootstrapVersion AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.
Parameters
と Rules
というセクションが追加されています。
これは CDK v2 で自動的に付与されるセクションのようです。
v1 から v2 の主な変更点のうち、以下に関するバリデーションと考えられます。
v2 ではデプロイ先の環境が最新のブートストラップスタックを使用して bootstrap されている必要がある
差分の確認
現在デプロイされているリソースとローカルの CDK プロジェクトの差分を調べます。
$ cdk diff Stack DevioStack Parameters [+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"} Other Changes [+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
上記のセクション部分のみ追加となっています。
その他のリソースに変更はかからないので意図通りです。
デプロイ
$ cdk deploy DevioStack: deploying... [0%] start: Publishing cdd63b4f8ae956987786be6dec1976ce054a108324f3aed5af09ffed9a488054:current_account-current_region [100%] success: Published cdd63b4f8ae956987786be6dec1976ce054a108324f3aed5af09ffed9a488054:current_account-current_region DevioStack: creating CloudFormation changeset... ✅ DevioStack Stack ARN: arn:aws:cloudformation:ap-northeast-1:060405383263:stack/DevioStack/d745be30-5984-11ec-802f-068875dc9763
デプロイできました。(リソースの変更は無し)
これにてマイグレーション作業完了です!
GitHub
今回のソースコードは コチラ です。
おわりに
v1 から v2 への移行は比較的簡単に実施できました。本シリーズでは L1 の CfnXXXX
クラスを利用しているため、影響が少なかったのかもしれません。
私は aws-cdk-lib
パッケージへの統一化だけで大満足です。これからも CDK を使って色々構築していきたいと思います。